#ifndef CUSTOM_POST_FX_PASSES_INCLUDED
#define CUSTOM_POST_FX_PASSES_INCLUDED

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Filtering.hlsl"
#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"


struct Varings {
	float4 positionCS : SV_POSITION;
	float2 screenUV : VAR_SCREEN_UV;
};
TEXTURE2D(_PostFXSource);
TEXTURE2D(_PostFXSource2);
SAMPLER(sampler_linear_clamp);
float4 _PostFXSource_TexelSize;
bool _BloomBicubicUpsampling;
float4 _BloomThreshold;
float _BloomIntensity;
float4 _ColorAdjustments;
float4 _ColorFilter;
float4 _WhiteBalance;
float4 _SplitToningShadows, _SplitToningHighlights;
float4 _ChannelMixerRed,_ChannelMixerGreen,_ChannelMixerBlue;
float4 _SMHShadows,_SMHMidtones,_SMHHightlights,_SMHRange;
TEXTURE2D(_ColorGradingLUT);
float4 _ColorGradingLUTParameters;
bool _ColorGradingLUTInLogC;



float Luminance(float3 color, bool useACES)
{
	return useACES ? AcesLuminance(color) : Luminance(color);
}


float4 GetSource(float2 screenUV)
{
	return SAMPLE_TEXTURE2D(_PostFXSource,sampler_linear_clamp,screenUV);
}


float4 GetSource2(float2 screenUV)
{
	return SAMPLE_TEXTURE2D(_PostFXSource2,sampler_linear_clamp,screenUV);
}

float4 GetSourceBicubic(float2 screenUV)
{
	return SampleTexture2DBicubic(
		TEXTURE2D_ARGS(_PostFXSource,sampler_linear_clamp),
		screenUV,
		_PostFXSource_TexelSize.zwxy,
		1.0,
		1.0
		);
}


float4 GetSourceTexelSize()
{
	return _PostFXSource_TexelSize;
}


Varings DefaultPassVertex(uint vertexID :SV_VertexID)
{
	Varings output;
	output.positionCS = float4(
		vertexID <=1 ? -1.0 : 3.0,
		vertexID ==1 ? 3.0 : -1.0,
		0.0,
		1.0
		);
	output.screenUV = float2(
		vertexID <=1 ? 0.0 :2.0,
		vertexID ==1 ? 2.0 :0.0
		);

	if(_ProjectioonParams.x < 0.0)
	{
		output.screenUV.y = 1.0 - output.screenUV.y;
	}
	return output;
}

float4 CopyPassFragment(Varings input) : SV_TARGET {
	return GetSource(input.screenUV);
}

float4 BloomHorizontalPassFragment(Varings input): SV_TARGET {
	float3 color = 0.0;
	float offsets[] = {
		-4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0
	};

	float weights[] = {
		0.01621622, 0.05405405, 0.12162162, 0.19459459, 0.22702703,
		0.19459459, 0.12162162, 0.05405405, 0.01621622
	};
	for( int i = 0; i < 9; i++)
	{
		float offset = offsets[i] * 2.0 * GetSourceTexelSize().x;		
		color += GetSource(input.screenUV + float2(offset,0.0) ).rgb * weights[i];
	}
	return float4(color,1.0);
}

float4 BloomVerticalPassFragment(Varings input): SV_TARGET {
	float3 color = 0.0;
	float offsets[] = {
		-4.0, -3.0, -2.0, -1.0, 0.0, 1.0, 2.0, 3.0, 4.0
	};

	float weights[] = {
		0.01621622, 0.05405405, 0.12162162, 0.19459459, 0.22702703,
		0.19459459, 0.12162162, 0.05405405, 0.01621622
	};
	for( int i = 0; i < 9; i++)
	{
		float offset = offsets[i] * 2.0 * GetSourceTexelSize().y;		
		color += GetSource(input.screenUV + float2(0.0,offset) ).rgb * weights[i];
	}
	return float4(color,1.0);
}

float4 BloomCombineAddPassFragment(Varings input) : SV_TARGET 
{
//	float3 lowRes = GetSource(input.screenUV).rgb;
	float3 lowRes;
	if(_BloomBicubicUpsampling)
	{
		lowRes = GetSourceBicubic(input.screenUV).rgb;
	}
	else
	{
		lowRes =  GetSource(input.screenUV).rgb;
	}
	
	float3 hightRes = GetSource2(input.screenUV).rgb;
	return float4(lowRes * _BloomIntensity + hightRes, 1.0);
}

float3 ApplyBloomThreshold(float3 color)
{
	float brightness = Max3(color.r,color.g,color.b);
	float soft = brightness + _BloomThreshold.y;
	soft  =clamp(soft, 0.0, _BloomThreshold.z);
	soft = soft * soft * _BloomThreshold.w;
	float contribution = max(soft, brightness - _BloomThreshold.x);
	contribution /= max(brightness,0.00001);
	return color * contribution;
}

float4 BloomPrefilterPassFragment(Varings input) : SV_TARGET {
	float3 color = ApplyBloomThreshold(GetSource(input.screenUV).rgb);
	return float4(color,1.0);
}


float4 BloomPrefilterFirefiesPassFragment(Varings input) : SV_TARGET
{
	float3 color = 0.0;
	float2 offsets[] = {
		float2(0,0), float2(-1.0,-1.0),float2(-1.0,1.0),float2(1.0,-1.0),float2(1.0,1.0)//,
		//float2(-1.0,0.0),float2(1.0,0.0),float2(0.0,-1.0),float2(0.0,1.0)
	};
	float weightSum = 0.0;
	for(int i=0; i < 5; i++)
	{
		float3 c = GetSource(input.screenUV + offsets[i] *  GetSourceTexelSize().xy * 2).rgb;		
		c = ApplyBloomThreshold(c);

		float w = 1.0/ (Luminance(c) + 1.0);
		color += c * w;
		weightSum += w;
	}
	color /= weightSum;
	return float4(color,1.0);
}



float4 BloomCombineScatterPassFragment(Varings input) : SV_TARGET 
{
//	float3 lowRes = GetSource(input.screenUV).rgb;
	float3 lowRes;
	if(_BloomBicubicUpsampling)
	{
		lowRes = GetSourceBicubic(input.screenUV).rgb;
	}
	else
	{
		lowRes =  GetSource(input.screenUV).rgb;
	}
	
	float3 hightRes = GetSource2(input.screenUV).rgb;
	return float4( lerp(hightRes,lowRes, _BloomIntensity), 1.0);
	//return float4(lowRes * _BloomIntensity + hightRes, 1.0);
}

float4 BloomScatterFinalPassFragment(Varings input) : SV_TARGET {
	
	float3 lowRes;
	if(_BloomBicubicUpsampling)
	{
		lowRes = GetSourceBicubic(input.screenUV).rgb;
	}
	else
	{
		lowRes =  GetSource(input.screenUV).rgb;
	}
	
	float3 hightRes = GetSource2(input.screenUV).rgb;
	lowRes += hightRes - ApplyBloomThreshold(hightRes);
	return float4( lerp(hightRes,lowRes, _BloomIntensity), 1.0);	
}


float3 ColorGradePostExposure(float3 color)
{
	return color * _ColorAdjustments.x;
}

float3 ColorGradeContrast(float3 color,bool useACES)
{
	color = useACES ? ACES_to_ACEScc(unity_to_ACES(color)) : LinearToLogC(color);
	color = (color - ACEScc_MIDGRAY) * _ColorAdjustments.y + ACEScc_MIDGRAY;
	return useACES ? ACES_to_ACEScg(ACEScc_to_ACES(color)) : LogCToLinear(color);
}

float3 ColorGradeColorFilter(float3 color)
{
	return color * _ColorFilter.rgb;
}

float3 ColorGradeHueShift(float3 color)
{
	color = RgbToHsv(color);
	float hue = color.x + _ColorAdjustments.z;
	color.x = RotateHue(hue, 0.0, 1.0);
	return HsvToRgb(color);
}

float3 ColorGradeSaturation(float3 color,bool useACES)
{
	float luminance  = Luminance(color,useACES);
	return (color - luminance) * _ColorAdjustments.w + luminance;
}

float3 ColorGradeWhiteBalance(float3 color)
{
	color = LinearToLMS(color);
	color *= _WhiteBalance.rgb;
	return LMSToLinear(color);
}

float3 ColorGradeSplitToning(float3 color,bool useACES)
{
	color = PositivePow(color,1.0/2.2);
	float t = saturate(Luminance(saturate(color),useACES) + _SplitToningShadows.w);
	float3 shadows = lerp(0.5,_SplitToningShadows.rgb,1.0- t);
	float3 highlights =  lerp(0.5,_SplitToningHighlights.rgb,t);
	color  = SoftLight(color,shadows);
	color  = SoftLight(color,highlights);
	return PositivePow(color,2.2);
}

float3 ColorGradingChannelMixer(float3 color)
{
	//float3x3 matrix = float3x3(_ChannelMixerRed.rgb,_ChannelMixerGreen.rgb,_ChannelMixerBlue.rgb);
	return mul(float3x3(_ChannelMixerRed.rgb,_ChannelMixerGreen.rgb,_ChannelMixerBlue.rgb),color);
}

float3 ColorGradingShadowsMidtonesHighlights(float3 color,bool useACES)
{
	float luminance = Luminance(color,useACES);
	float shadowsWeight = 1.0 - smoothstep(_SMHRange.x,_SMHRange.y,luminance);
	float highlightsWeight = smoothstep(_SMHRange.z,_SMHRange.w,luminance);
	float midtoneWeight = 1.0 - shadowsWeight - highlightsWeight;

	return color * _SMHShadows.rgb * shadowsWeight
	+ color * _SMHHightlights.rgb * highlightsWeight
	+color * _SMHMidtones.rgb * midtoneWeight;

}

float3 ColorGrade(float3 color,bool useACES = false)
{
//	color = min(color,60.0);
	color = ColorGradePostExposure(color);
	color = ColorGradeWhiteBalance(color);
	color = ColorGradeContrast(color,useACES);	
	color = ColorGradeColorFilter(color);
	color = max(color,0.0);
	color = ColorGradeSplitToning(color,useACES);
	color = ColorGradingChannelMixer(color);
	color = max(color,0.0);
	color = ColorGradingShadowsMidtonesHighlights(color,useACES);
	color = ColorGradeHueShift(color);
	color = ColorGradeSaturation(color,useACES);	
	color = max(useACES?ACEScg_to_ACES( color):color , 0.0 );
	return color;
}



float4 ToneMappingNonePassFragment(Varings input) : SV_TARGET 
{
	float4 color = GetSource(input.screenUV);
	color.rgb = ColorGrade(color);
	
	return color;
}

float4 ToneMappingReinhardPassFragment(Varings input) : SV_TARGET 
{
	float4 color = GetSource(input.screenUV);
	color.rgb = ColorGrade(color);
	color.rgb /= color.rgb + 1.0;
	return color;
}

float4 ToneMappingNeutralPassFragment(Varings input) : SV_TARGET 
{
	float4 color = GetSource(input.screenUV);
	color.rgb = ColorGrade(color);
	//color.rgb /= color.rgb + 1.0;
	color.rgb = NeutralTonemap(color.rgb);
	return color;
}

float4 ToneMappingACESPassFragment(Varings input) : SV_TARGET 
{
	float4 color = GetSource(input.screenUV);
	color.rgb = ColorGrade(color,true);
	//color.rgb /= color.rgb + 1.0;
	//color.rgb = AcesTonemap(unity_to_ACES(color.rgb));
	color.rgb = AcesTonemap(color.rgb);
	return color;
}

float3 GetColorGradedLUT(float2 uv, bool useACES = false)
{
	//float3 color = float3(uv,0.0);
	float3 color = GetLutStripValue(uv,_ColorGradingLUTParameters);	
	return ColorGrade(_ColorGradingLUTInLogC ? LogCToLinear(color): color ,useACES);
}

float4 ColorGradingNonePassFragment(Varings input) : SV_TARGET 
{
	float3 color = GetColorGradedLUT(input.screenUV);
	return float4(color,1.0);
}

float4 ColorGradingReinhardPassFragment(Varings input) : SV_TARGET 
{
	float3 color = GetColorGradedLUT(input.screenUV);
	color.rgb /= color.rgb + 1.0;
	return float4(color,1.0);
}

float4 ColorGradingNeutralPassFragment(Varings input) : SV_TARGET 
{
	float3 color = GetColorGradedLUT(input.screenUV);
	color.rgb = NeutralTonemap(color.rgb);
	return float4(color,1.0);
}

float4 ColorGradingACESPassFragment(Varings input) : SV_TARGET 
{
	float3 color = GetColorGradedLUT(input.screenUV);
	color.rgb = AcesTonemap(color.rgb);
	return float4(co lor,1.0);
}

float3 ApplyColorGradingLUT(float3 color)
{
	color = ApplyLut2D(
		TEXTURE2D_ARGS(_ColorGradingLUT,sampler_linear_clamp),
		saturate(_ColorGradingLUTInLogC ? LinearToLogC(color): color),
		_ColorGradingLUTParameters.xyz
		);
	return color;
}

float4 FinalPassFragment(Varings input) :SV_TARGET
{
	float4 color = GetSource(input.screenUV);
	color.rgb = ApplyColorGradingLUT(color);
	return color;
}
#endif